package com.nbcio.modules.erp.inventory.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.nbcio.common.satoken.utils.LoginHelper;
import com.nbcio.modules.erp.doc.domain.ErpBill;
import com.nbcio.modules.erp.doc.domain.ErpBillItem;
import com.nbcio.modules.erp.doc.service.IErpBillItemService;
import com.nbcio.modules.erp.inventory.domain.ErpInvBalance;
import com.nbcio.modules.erp.inventory.domain.ErpInvLedger;
import com.nbcio.modules.erp.inventory.mapper.ErpInvBalanceMapper;
import com.nbcio.modules.erp.inventory.mapper.ErpInvLedgerMapper;
import com.nbcio.modules.erp.inventory.service.IErpInventoryService;
import com.nbcio.modules.erp.material.domain.ErpMaterial;
import com.nbcio.modules.erp.material.mapper.ErpMaterialMapper;
import com.nbcio.modules.erp.uom.domain.ErpUomConv;
import com.nbcio.modules.erp.uom.mapper.ErpUomConvMapper;
import com.nbcio.modules.erp.voucher.domain.ErpVoucher;
import com.nbcio.modules.erp.voucher.domain.ErpVoucherEntry;
import com.nbcio.modules.erp.voucher.mapper.ErpVoucherEntryMapper;
import com.nbcio.modules.erp.voucher.mapper.ErpVoucherMapper;
import com.nbcio.modules.erp.voucher.service.IErpAccountConfigService;
import com.nbcio.modules.erp.coderule.service.ICodeRuleService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.List;

/**
 * 库存服务实现
 */
@Slf4j
@Service
public class ErpInventoryServiceImpl implements IErpInventoryService {

    @Resource
    private ErpInvLedgerMapper ledgerMapper;
    @Resource
    private ErpInvBalanceMapper balanceMapper;
    @Resource
    private ErpMaterialMapper materialMapper;
    @Resource
    private ErpUomConvMapper uomConvMapper;
    @Resource
    private IErpBillItemService billItemService;
    @Resource
    private ErpVoucherMapper voucherMapper;
    @Resource
    private ErpVoucherEntryMapper voucherEntryMapper;
    @Resource
    private ICodeRuleService codeRuleService;
    @Resource
    private IErpAccountConfigService accountConfigService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void postBill(ErpBill bill) {
        // 检查科目配置（过账前必须配置好科目）
        validateAccountConfig(bill.getBillType());
        
        // 判断出入库方向
        boolean isIn = "PO_IN".equals(bill.getBillType()); // 采购入库为IN，销售出库为OUT
        
        // 查询单据明细
        List<ErpBillItem> items = billItemService.lambdaQuery()
                .eq(ErpBillItem::getBillId, bill.getBillId())
                .orderByAsc(ErpBillItem::getLineNo)
                .list();
        
        if (items.isEmpty()) {
            return;
        }

        LocalDateTime now = LocalDateTime.now();
        Long userId = LoginHelper.getUserId();
        
        // 用于保存每个明细的成本信息（用于生成凭证）
        List<ItemCostInfo> costInfoList = new java.util.ArrayList<>();

        // 遍历明细进行过账
        for (ErpBillItem item : items) {
            // 获取物料信息（含基本单位）
            ErpMaterial material = materialMapper.selectById(item.getMaterialId());
            if (material == null) continue;
            
            Long baseUomId = material.getUomId(); // 基本单位ID
            Long itemUomId = item.getUomId(); // 明细单位ID
            
            // 计算单位换算率（明细单位 → 基本单位）
            BigDecimal rate = BigDecimal.ONE; // 默认1
            if (!baseUomId.equals(itemUomId)) {
                // 查找单位换算：从明细单位到基本单位
                ErpUomConv conv = uomConvMapper.selectOne(
                    new LambdaQueryWrapper<ErpUomConv>()
                        .eq(ErpUomConv::getMaterialId, item.getMaterialId())
                        .eq(ErpUomConv::getFromUomId, itemUomId)
                        .eq(ErpUomConv::getToUomId, baseUomId)
                        .last("LIMIT 1")
                );
                if (conv != null) {
                    rate = conv.getRate();
                } else {
                    // 反向查找：从基本单位到明细单位，取倒数
                    ErpUomConv reverseConv = uomConvMapper.selectOne(
                        new LambdaQueryWrapper<ErpUomConv>()
                            .eq(ErpUomConv::getMaterialId, item.getMaterialId())
                            .eq(ErpUomConv::getFromUomId, baseUomId)
                            .eq(ErpUomConv::getToUomId, itemUomId)
                            .last("LIMIT 1")
                    );
                    if (reverseConv != null) {
                        rate = BigDecimal.ONE.divide(reverseConv.getRate(), 18, RoundingMode.HALF_UP);
                    }
                }
            }
            
            // 转换为基本单位数量
            BigDecimal baseQty = item.getQty().multiply(rate);
            
            // 计算基本单位单价（未税）
            BigDecimal baseUnitPrice = item.getUnitPriceExcl();
            if (baseUnitPrice == null || baseUnitPrice.compareTo(BigDecimal.ZERO) == 0) {
                // 如果未税单价为空，从含税单价和税率计算
                BigDecimal taxRatePct = item.getTaxRate() != null ? 
                    item.getTaxRate().divide(BigDecimal.valueOf(100), 18, RoundingMode.HALF_UP) : BigDecimal.ZERO;
                BigDecimal unitPriceIncl = item.getUnitPriceIncl() != null ? item.getUnitPriceIncl() : BigDecimal.ZERO;
                if (unitPriceIncl.compareTo(BigDecimal.ZERO) > 0 && taxRatePct.compareTo(BigDecimal.ZERO) > 0) {
                    baseUnitPrice = unitPriceIncl.divide(BigDecimal.ONE.add(taxRatePct), 18, RoundingMode.HALF_UP);
                } else {
                    baseUnitPrice = item.getPrice() != null ? item.getPrice() : BigDecimal.ZERO;
                }
            }
            // 如果明细单位不是基本单位，需要换算单价
            if (!baseUomId.equals(itemUomId) && rate.compareTo(BigDecimal.ONE) != 0) {
                baseUnitPrice = baseUnitPrice.divide(rate, 18, RoundingMode.HALF_UP);
            }
            
            // 计算金额（基本单位）
            BigDecimal baseAmount = baseQty.multiply(baseUnitPrice);
            BigDecimal taxAmount = item.getLineTaxAmount() != null ? item.getLineTaxAmount() : BigDecimal.ZERO;
            
            // 1. 写入库存台账
            ErpInvLedger ledger = new ErpInvLedger();
            ledger.setBizTime(bill.getApproveTime() != null ? bill.getApproveTime() : now);
            ledger.setBillType(bill.getBillType());
            ledger.setBillId(bill.getBillId());
            ledger.setBillNo(bill.getBillNo());
            ledger.setIoType(isIn ? "IN" : "OUT");
            ledger.setOrgId(bill.getOrgId());
            ledger.setWarehouseId(bill.getWarehouseId());
            ledger.setMaterialId(item.getMaterialId());
            ledger.setLotId(item.getLotId());
            ledger.setSerialId(item.getSerialId());
            ledger.setBinCode(null); // 货位待扩展
            if (isIn) {
                ledger.setQtyIn(baseQty);
                ledger.setQtyOut(BigDecimal.ZERO);
                ledger.setAmountIn(baseAmount);
                ledger.setAmountOut(BigDecimal.ZERO);
            } else {
                ledger.setQtyIn(BigDecimal.ZERO);
                ledger.setQtyOut(baseQty);
                ledger.setAmountIn(BigDecimal.ZERO);
                ledger.setAmountOut(baseAmount);
            }
            ledger.setTaxAmount(taxAmount);
            ledger.setCostMethod("MOVING_AVG");
            ledger.setCreateTime(now);
            ledgerMapper.insert(ledger);
            
            // 2. 更新库存余额（移动加权平均成本）
            String balanceKey = buildBalanceKey(bill.getWarehouseId(), item.getMaterialId(), 
                    item.getLotId(), item.getSerialId(), null);
            
            LambdaQueryWrapper<ErpInvBalance> balanceQw = new LambdaQueryWrapper<ErpInvBalance>()
                    .eq(ErpInvBalance::getWarehouseId, bill.getWarehouseId())
                    .eq(ErpInvBalance::getMaterialId, item.getMaterialId());
            if (item.getLotId() != null) {
                balanceQw.eq(ErpInvBalance::getLotId, item.getLotId());
            } else {
                balanceQw.isNull(ErpInvBalance::getLotId);
            }
            if (item.getSerialId() != null) {
                balanceQw.eq(ErpInvBalance::getSerialId, item.getSerialId());
            } else {
                balanceQw.isNull(ErpInvBalance::getSerialId);
            }
            balanceQw.last("LIMIT 1");
            ErpInvBalance balance = balanceMapper.selectOne(balanceQw);
            
            BigDecimal newQtyOnHand;
            BigDecimal newAmount;
            BigDecimal newMovingAvgCost;
            
            if (balance == null) {
                // 新建余额记录
                balance = new ErpInvBalance();
                balance.setOrgId(bill.getOrgId());
                balance.setWarehouseId(bill.getWarehouseId());
                balance.setMaterialId(item.getMaterialId());
                balance.setLotId(item.getLotId());
                balance.setSerialId(item.getSerialId());
                balance.setBinCode(null);
                if (isIn) {
                    newQtyOnHand = baseQty;
                    newAmount = baseAmount;
                    newMovingAvgCost = baseUnitPrice;
                } else {
                    // 出库时余额不能为负
                    throw new RuntimeException("库存余额不足，无法出库");
                }
                balance.setQtyOnHand(newQtyOnHand);
                balance.setAmount(newAmount);
                balance.setMovingAvgCost(newMovingAvgCost);
                balance.setUpdateTime(now);
                balanceMapper.insert(balance);
            } else {
                // 更新余额记录（移动加权平均）
                BigDecimal oldQty = balance.getQtyOnHand();
                BigDecimal oldAmount = balance.getAmount();
                
                if (isIn) {
                    // 入库：移动加权平均成本 = (原金额 + 本次金额) / (原数量 + 本次数量)
                    newQtyOnHand = oldQty.add(baseQty);
                    newAmount = oldAmount.add(baseAmount);
                    if (newQtyOnHand.compareTo(BigDecimal.ZERO) > 0) {
                        newMovingAvgCost = newAmount.divide(newQtyOnHand, 6, RoundingMode.HALF_UP);
                    } else {
                        newMovingAvgCost = BigDecimal.ZERO;
                    }
                } else {
                    // 出库：使用当前移动加权平均成本计算实际成本
                    newMovingAvgCost = balance.getMovingAvgCost(); // 出库不改变单价
                    BigDecimal actualCostAmount = baseQty.multiply(newMovingAvgCost); // 实际成本金额
                    
                    // 保存成本信息用于生成凭证
                    costInfoList.add(new ItemCostInfo(item.getMaterialId(), baseQty, actualCostAmount, newMovingAvgCost));
                    
                    newQtyOnHand = oldQty.subtract(baseQty);
                    if (newQtyOnHand.compareTo(BigDecimal.ZERO) < 0) {
                        throw new RuntimeException("库存余额不足，无法出库");
                    }
                    newAmount = newQtyOnHand.multiply(newMovingAvgCost);
                }
                
                balance.setQtyOnHand(newQtyOnHand);
                balance.setAmount(newAmount);
                balance.setMovingAvgCost(newMovingAvgCost);
                balance.setUpdateTime(now);
                balanceMapper.updateById(balance);
            }
            
            // 更新台账的过账后移动平均单价
            ledger.setUnitCostAfter(newMovingAvgCost);
            ledgerMapper.updateById(ledger);
        }
        
        // 3. 生成财务凭证
        generateVoucher(bill, items, costInfoList, userId);
    }
    
    /**
     * 明细成本信息（用于凭证生成）
     */
    private static class ItemCostInfo {
        Long materialId;
        BigDecimal qty;
        BigDecimal costAmount;
        BigDecimal unitCost;
        
        ItemCostInfo(Long materialId, BigDecimal qty, BigDecimal costAmount, BigDecimal unitCost) {
            this.materialId = materialId;
            this.qty = qty;
            this.costAmount = costAmount;
            this.unitCost = unitCost;
        }
    }
    
    private String buildBalanceKey(Long warehouseId, Long materialId, Long lotId, Long serialId, String binCode) {
        return String.format("%d_%d_%s_%s_%s", warehouseId, materialId, 
                lotId != null ? lotId : "", serialId != null ? serialId : "", binCode != null ? binCode : "");
    }
    
    /**
     * 校验科目配置
     * 在过账前必须配置好所有必需的科目，否则无法生成财务凭证
     * 
     * @param billType 单据类型
     * @throws RuntimeException 如果缺少必需的科目配置
     */
    private void validateAccountConfig(String billType) {
        java.util.List<String> missingConfigs = new java.util.ArrayList<>();
        
        if ("PO_IN".equals(billType)) {
            // 采购入库需要的科目配置
            if (accountConfigService.getAccountId(billType, "STOCK") == null) {
                missingConfigs.add("库存商品(STOCK)");
            }
            if (accountConfigService.getAccountId(billType, "TAX_IN") == null) {
                missingConfigs.add("进项税额(TAX_IN)");
            }
            if (accountConfigService.getAccountId(billType, "PAYABLE") == null) {
                missingConfigs.add("应付账款(PAYABLE)");
            }
        } else if ("SO_OUT".equals(billType)) {
            // 销售出库需要的科目配置
            if (accountConfigService.getAccountId(billType, "STOCK") == null) {
                missingConfigs.add("库存商品(STOCK)");
            }
            if (accountConfigService.getAccountId(billType, "COST") == null) {
                missingConfigs.add("主营业务成本(COST)");
            }
            if (accountConfigService.getAccountId(billType, "RECEIVABLE") == null) {
                missingConfigs.add("应收账款(RECEIVABLE)");
            }
            if (accountConfigService.getAccountId(billType, "REVENUE") == null) {
                missingConfigs.add("主营业务收入(REVENUE)");
            }
            if (accountConfigService.getAccountId(billType, "TAX_OUT") == null) {
                missingConfigs.add("销项税额(TAX_OUT)");
            }
        }
        
        if (!missingConfigs.isEmpty()) {
            String billTypeName = "PO_IN".equals(billType) ? "采购入库" : "销售出库";
            String missingList = String.join("、", missingConfigs);
            String errorMsg = String.format(
                "过账失败：%s单据缺少科目配置。\n\n" +
                "缺少的科目配置：%s\n\n" +
                "请按以下步骤操作：\n" +
                "1. 进入【ERP管理 > 科目配置】菜单\n" +
                "2. 为【%s】单据类型配置上述科目\n" +
                "3. 或者执行测试数据脚本快速初始化：\n" +
                "   script/sql/mysql/erp_test_account.sql",
                billTypeName, missingList, billTypeName
            );
            throw new RuntimeException(errorMsg);
        }
    }
    
    /**
     * 生成财务凭证
     */
    private void generateVoucher(ErpBill bill, List<ErpBillItem> items, List<ItemCostInfo> costInfoList, Long userId) {
        ErpVoucher voucher = new ErpVoucher();
        String voucherNo = codeRuleService.nextBillNo("VOUCHER");
        voucher.setVoucherNo(voucherNo);
        voucher.setVoucherDate(bill.getApproveTime() != null ? bill.getApproveTime().toLocalDate() : LocalDateTime.now().toLocalDate());
        voucher.setVoucherWord("记");
        voucher.setAttachCount(0);
        voucher.setBillType(bill.getBillType());
        voucher.setBillId(bill.getBillId());
        voucher.setBillNo(bill.getBillNo());
        voucher.setStatus("POSTED");
        voucher.setPostTime(LocalDateTime.now());
        voucher.setPostUserId(userId);
        voucher.setRemark(bill.getRemark());
        voucher.setCreateTime(LocalDateTime.now());
        voucher.setUpdateTime(LocalDateTime.now());
        
        BigDecimal totalDebit = BigDecimal.ZERO;
        BigDecimal totalCredit = BigDecimal.ZERO;
        int entryNo = 1;
        List<ErpVoucherEntry> entries = new java.util.ArrayList<>();
        
        // 从科目配置获取科目ID（已经在validateAccountConfig中校验过，这里直接使用，不再使用默认值）
        Long stockAccountId = accountConfigService.getAccountId(bill.getBillType(), "STOCK");
        Long payAccountId = accountConfigService.getAccountId(bill.getBillType(), "PAYABLE");
        Long receivableAccountId = accountConfigService.getAccountId(bill.getBillType(), "RECEIVABLE");
        Long revenueAccountId = accountConfigService.getAccountId(bill.getBillType(), "REVENUE");
        Long costAccountId = accountConfigService.getAccountId(bill.getBillType(), "COST");
        Long taxInAccountId = accountConfigService.getAccountId(bill.getBillType(), "TAX_IN");
        Long taxOutAccountId = accountConfigService.getAccountId(bill.getBillType(), "TAX_OUT");
        
        // 理论上不应该为空（因为已经在校验中检查过），但为了安全起见，如果仍然为空则抛出异常
        if (stockAccountId == null) {
            throw new RuntimeException("科目配置错误：库存商品科目未配置");
        }
        
        if ("PO_IN".equals(bill.getBillType())) {
            // 采购入库：借方库存商品+税额，贷方应付账款
            // 计算行金额：优先使用amount字段，如果没有则使用unitPriceExcl * qty，最后使用price * qty
            log.info("开始生成采购入库凭证，单据号: {}, 明细数量: {}", bill.getBillNo(), items.size());
            
            BigDecimal totalAmount = BigDecimal.ZERO;
            for (ErpBillItem item : items) {
                BigDecimal itemAmount = BigDecimal.ZERO;
                
                // 优先使用amount字段（行金额）
                if (item.getAmount() != null && item.getAmount().compareTo(BigDecimal.ZERO) > 0) {
                    itemAmount = item.getAmount();
                    log.debug("使用amount字段: {}", itemAmount);
                }
                // 其次使用unitPriceExcl * qty
                else if (item.getUnitPriceExcl() != null && item.getQty() != null && 
                         item.getUnitPriceExcl().compareTo(BigDecimal.ZERO) > 0) {
                    itemAmount = item.getUnitPriceExcl().multiply(item.getQty());
                    log.debug("使用unitPriceExcl * qty: {} * {} = {}", item.getUnitPriceExcl(), item.getQty(), itemAmount);
                }
                // 最后使用price * qty（作为兜底）
                else if (item.getPrice() != null && item.getQty() != null && 
                         item.getPrice().compareTo(BigDecimal.ZERO) > 0) {
                    if (item.getTaxRate() != null && item.getTaxRate().compareTo(BigDecimal.ZERO) > 0) {
                        // 如果price是含税的，需要先转换为未税价
                        BigDecimal taxRate = item.getTaxRate().divide(new BigDecimal("100"));
                        BigDecimal unitExcl = item.getPrice().divide(BigDecimal.ONE.add(taxRate), 6, java.math.RoundingMode.HALF_UP);
                        itemAmount = unitExcl.multiply(item.getQty());
                        log.debug("使用price转换为未税价 * qty: {} (含税) -> {} (未税) * {} = {}", 
                                item.getPrice(), unitExcl, item.getQty(), itemAmount);
                    } else {
                        itemAmount = item.getPrice().multiply(item.getQty());
                        log.debug("使用price * qty: {} * {} = {}", item.getPrice(), item.getQty(), itemAmount);
                    }
                } else {
                    log.warn("明细行{}无法计算金额，amount={}, unitPriceExcl={}, price={}, qty={}", 
                            item.getLineNo(), item.getAmount(), item.getUnitPriceExcl(), item.getPrice(), item.getQty());
                }
                
                totalAmount = totalAmount.add(itemAmount);
            }
            
            log.info("采购入库凭证总金额（未税）: {}, 明细数量: {}", totalAmount, items.size());
            BigDecimal totalTax = items.stream()
                    .map(item -> item.getLineTaxAmount() != null ? item.getLineTaxAmount() : BigDecimal.ZERO)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            
            // 借方：库存商品（未税金额）
            ErpVoucherEntry entry1 = new ErpVoucherEntry();
            entry1.setEntryNo(entryNo++);
            entry1.setAccountId(stockAccountId);
            entry1.setDcFlag("DEBIT");
            entry1.setAmount(totalAmount);
            entry1.setSummary(bill.getBillNo() + "-采购入库");
            entry1.setCreateTime(LocalDateTime.now());
            entries.add(entry1);
            totalDebit = totalDebit.add(totalAmount);
            
            // 借方：进项税额（如果有）
            if (totalTax.compareTo(BigDecimal.ZERO) > 0) {
                ErpVoucherEntry entry2 = new ErpVoucherEntry();
                entry2.setEntryNo(entryNo++);
                entry2.setAccountId(taxInAccountId);
                entry2.setDcFlag("DEBIT");
                entry2.setAmount(totalTax);
                entry2.setSummary(bill.getBillNo() + "-进项税额");
                entry2.setCreateTime(LocalDateTime.now());
                entries.add(entry2);
                totalDebit = totalDebit.add(totalTax);
            }
            
            // 贷方：应付账款
            ErpVoucherEntry entry3 = new ErpVoucherEntry();
            entry3.setEntryNo(entryNo++);
            entry3.setAccountId(payAccountId);
            entry3.setDcFlag("CREDIT");
            entry3.setAmount(totalAmount.add(totalTax));
            entry3.setSummary(bill.getBillNo() + "-应付账款");
            entry3.setCreateTime(LocalDateTime.now());
            entries.add(entry3);
            totalCredit = totalCredit.add(totalAmount.add(totalTax));
        } else if ("SO_OUT".equals(bill.getBillType())) {
            // 销售出库：借方主营业务成本，贷方库存商品；借方应收账款，贷方主营业务收入+销项税额
            
            // 计算实际成本（从库存余额获取）
            BigDecimal totalCostAmount = BigDecimal.ZERO;
            if (costInfoList != null && !costInfoList.isEmpty()) {
                totalCostAmount = costInfoList.stream()
                        .map(info -> info.costAmount)
                        .reduce(BigDecimal.ZERO, BigDecimal::add);
            } else {
                // 如果没有成本信息，回退到使用销售金额（不应该发生）
                totalCostAmount = items.stream()
                        .map(item -> item.getUnitPriceExcl() != null && item.getQty() != null ? 
                            item.getUnitPriceExcl().multiply(item.getQty()) : BigDecimal.ZERO)
                        .reduce(BigDecimal.ZERO, BigDecimal::add);
            }
            
            // 计算销售收入和税额
            // 优先使用amount字段，如果没有则使用unitPriceExcl * qty，最后使用price * qty
            log.info("开始生成销售出库凭证，单据号: {}, 明细数量: {}", bill.getBillNo(), items.size());
            
            BigDecimal totalAmount = BigDecimal.ZERO;
            for (ErpBillItem item : items) {
                BigDecimal itemAmount = BigDecimal.ZERO;
                
                // 优先使用amount字段（行金额）
                if (item.getAmount() != null && item.getAmount().compareTo(BigDecimal.ZERO) > 0) {
                    itemAmount = item.getAmount();
                    log.debug("使用amount字段: {}", itemAmount);
                }
                // 其次使用unitPriceExcl * qty
                else if (item.getUnitPriceExcl() != null && item.getQty() != null && 
                         item.getUnitPriceExcl().compareTo(BigDecimal.ZERO) > 0) {
                    itemAmount = item.getUnitPriceExcl().multiply(item.getQty());
                    log.debug("使用unitPriceExcl * qty: {} * {} = {}", item.getUnitPriceExcl(), item.getQty(), itemAmount);
                }
                // 最后使用price * qty（作为兜底）
                else if (item.getPrice() != null && item.getQty() != null && 
                         item.getPrice().compareTo(BigDecimal.ZERO) > 0) {
                    if (item.getTaxRate() != null && item.getTaxRate().compareTo(BigDecimal.ZERO) > 0) {
                        // 如果price是含税的，需要先转换为未税价
                        BigDecimal taxRate = item.getTaxRate().divide(new BigDecimal("100"));
                        BigDecimal unitExcl = item.getPrice().divide(BigDecimal.ONE.add(taxRate), 6, java.math.RoundingMode.HALF_UP);
                        itemAmount = unitExcl.multiply(item.getQty());
                        log.debug("使用price转换为未税价 * qty: {} (含税) -> {} (未税) * {} = {}", 
                                item.getPrice(), unitExcl, item.getQty(), itemAmount);
                    } else {
                        itemAmount = item.getPrice().multiply(item.getQty());
                        log.debug("使用price * qty: {} * {} = {}", item.getPrice(), item.getQty(), itemAmount);
                    }
                } else {
                    log.warn("明细行{}无法计算金额，amount={}, unitPriceExcl={}, price={}, qty={}", 
                            item.getLineNo(), item.getAmount(), item.getUnitPriceExcl(), item.getPrice(), item.getQty());
                }
                
                totalAmount = totalAmount.add(itemAmount);
            }
            
            log.info("销售出库凭证总金额（未税）: {}, 明细数量: {}", totalAmount, items.size());
            
            BigDecimal totalTax = items.stream()
                    .map(item -> item.getLineTaxAmount() != null ? item.getLineTaxAmount() : BigDecimal.ZERO)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            
            log.info("销售出库凭证总税额: {}", totalTax);
            
            // 借方：主营业务成本（使用实际成本）
            ErpVoucherEntry entry1 = new ErpVoucherEntry();
            entry1.setEntryNo(entryNo++);
            entry1.setAccountId(costAccountId);
            entry1.setDcFlag("DEBIT");
            entry1.setAmount(totalCostAmount);
            entry1.setSummary(bill.getBillNo() + "-主营业务成本");
            entry1.setCreateTime(LocalDateTime.now());
            entries.add(entry1);
            totalDebit = totalDebit.add(totalCostAmount);
            
            // 贷方：库存商品（使用实际成本）
            ErpVoucherEntry entry2 = new ErpVoucherEntry();
            entry2.setEntryNo(entryNo++);
            entry2.setAccountId(stockAccountId);
            entry2.setDcFlag("CREDIT");
            entry2.setAmount(totalCostAmount);
            entry2.setSummary(bill.getBillNo() + "-出库");
            entry2.setCreateTime(LocalDateTime.now());
            entries.add(entry2);
            totalCredit = totalCredit.add(totalCostAmount);
            
            // 借方：应收账款
            ErpVoucherEntry entry3 = new ErpVoucherEntry();
            entry3.setEntryNo(entryNo++);
            entry3.setAccountId(receivableAccountId);
            entry3.setDcFlag("DEBIT");
            entry3.setAmount(totalAmount.add(totalTax));
            entry3.setSummary(bill.getBillNo() + "-应收账款");
            entry3.setCreateTime(LocalDateTime.now());
            entries.add(entry3);
            totalDebit = totalDebit.add(totalAmount.add(totalTax));
            
            // 贷方：主营业务收入
            ErpVoucherEntry entry4 = new ErpVoucherEntry();
            entry4.setEntryNo(entryNo++);
            entry4.setAccountId(revenueAccountId);
            entry4.setDcFlag("CREDIT");
            entry4.setAmount(totalAmount);
            entry4.setSummary(bill.getBillNo() + "-主营业务收入");
            entry4.setCreateTime(LocalDateTime.now());
            entries.add(entry4);
            totalCredit = totalCredit.add(totalAmount);
            
            // 贷方：销项税额
            if (totalTax.compareTo(BigDecimal.ZERO) > 0) {
                ErpVoucherEntry entry5 = new ErpVoucherEntry();
                entry5.setEntryNo(entryNo++);
                entry5.setAccountId(taxOutAccountId);
                entry5.setDcFlag("CREDIT");
                entry5.setAmount(totalTax);
                entry5.setSummary(bill.getBillNo() + "-销项税额");
                entry5.setCreateTime(LocalDateTime.now());
                entries.add(entry5);
                totalCredit = totalCredit.add(totalTax);
            }
        }
        
        voucher.setTotalDebit(totalDebit);
        voucher.setTotalCredit(totalCredit);
        
        // 插入凭证（如果凭证号重复则重试）
        int maxRetries = 5;
        boolean inserted = false;
        for (int i = 0; i < maxRetries && !inserted; i++) {
            try {
                voucherMapper.insert(voucher); // MyBatis-Plus 会自动回填主键
                inserted = true;
            } catch (org.springframework.dao.DuplicateKeyException e) {
                // 凭证号重复，重新生成
                if (i < maxRetries - 1) {
                    voucherNo = codeRuleService.nextBillNo("VOUCHER");
                    voucher.setVoucherNo(voucherNo);
                    // 短暂延迟后重试
                    try {
                        Thread.sleep(50 + (i * 10));
                    } catch (InterruptedException ie) {
                        Thread.currentThread().interrupt();
                    }
                } else {
                    // 最后一次重试仍然失败，使用时间戳后缀
                    voucherNo = voucherNo + "-" + (System.currentTimeMillis() % 10000);
                    voucher.setVoucherNo(voucherNo);
                    voucherMapper.insert(voucher);
                    inserted = true;
                }
            }
        }
        
        // 保存凭证明细
        for (ErpVoucherEntry entry : entries) {
            entry.setVoucherId(voucher.getVoucherId());
            voucherEntryMapper.insert(entry);
        }
    }
}

